package com.xsdl.combination.expression;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class ExpressionParse {

    private static final Set<Character> operatorSet = Set.of('+', '-', '*', '/');

    private final String expression;

    public ExpressionParse(String expression) {
        this.expression = expression;
    }

    public List<String> toSuffix() {
        int point = 0;
        List<String> result = new ArrayList<>();
        LinkedList<String> stack = new LinkedList<>();
        while (point < expression.length()) {
            char c = expression.charAt(point);
            if (c == '(') {
                stack.addLast(c + "");
            } else if (c == ')') {
                while (!stack.isEmpty() && !stack.getLast().equals("(")) {
                    result.add(stack.removeLast());
                }
                stack.removeLast();
            } else if (isOperator(c)) {
                if (c == '+' || c == '-') {
                    while (!stack.isEmpty() && isOperator(stack.getLast().charAt(0))) {
                        result.add(stack.removeLast());
                    }
                }
                if (c == '+' || c == '-') {
                    while (!stack.isEmpty() && (stack.getLast().equals("*") || stack.getLast().equals("/"))) {
                        result.add(stack.removeLast());
                    }
                }
                stack.add(c + "");
            } else {
                StringBuilder builder = new StringBuilder();
                while (point < expression.length() && Character.isDigit(expression.charAt(point))) {
                    builder.append(expression.charAt(point));
                    point++;
                }
                result.add(builder.toString());
                point--;
            }
            point++;
        }
        while (!stack.isEmpty()) {
            result.add(stack.removeLast());
        }
        return result;
    }

    private boolean isOperator(char c) {
        return operatorSet.contains(c);
    }

    public Expression parse() {
        List<String> list = toSuffix();
        LinkedList<Expression> stack = new LinkedList<>();
        for (String item : list) {
            if (isOperator(item.charAt(0))) {
                Expression right = stack.removeLast();
                switch (item) {
                    case "+": {
                        stack.add(new AddExpression(stack.removeLast(), right));
                        break;
                    }
                    case "-": {
                        stack.add(new SubExpression(stack.removeLast(), right));
                        break;
                    }
                    case "*": {
                        stack.add(new MultiplyExpression(stack.removeLast(), right));
                        break;
                    }
                    case "/": {
                        stack.add(new DivisionExpression(stack.removeLast(), right));
                        break;
                    }
                }
            } else {
                stack.addLast(new NumberExpression(Integer.parseInt(item)));
            }
        }
        return stack.getLast();
    }

}
